/**
 Copyright (c) 2015-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree. An additional grant
 of patent rights can be found in the PATENTS file in the same directory.
 */

#include <gtest/gtest.h>
#include <graphics/Format/PNG.h>
#include <graphics/Image.h>
#include <graphics/PixelFormat.h>

using graphics::Format::PNG;
using graphics::Image;
using graphics::PixelFormat;

/* Gray */
static void
Gray(std::vector<uint8_t> *png, std::vector<uint8_t> *pixels, PixelFormat *pixelFormat)
{
    *png = {
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
        0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
        0x08, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x7e, 0x9b, 0x55, 0x00, 0x00, 0x00,
        0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xc8, 0x02, 0x00, 0x00,
        0x6c, 0x00, 0x6b, 0x9e, 0x0d, 0xf6, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x49,
        0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
    };

    *pixels = {
        0x6A,
    };

    *pixelFormat = PixelFormat(
        PixelFormat::Color::Grayscale,
        PixelFormat::Order::Forward,
        PixelFormat::Alpha::None);
}

/* Gray Alpha */
static void
GrayAlpha(std::vector<uint8_t> *png, std::vector<uint8_t> *pixels, PixelFormat *pixelFormat)
{
    *png = {
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
        0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
        0x08, 0x04, 0x00, 0x00, 0x00, 0xb5, 0x1c, 0x0c, 0x02, 0x00, 0x00, 0x00,
        0x0b, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xc8, 0xf2, 0x03, 0x00,
        0x01, 0x25, 0x00, 0xb9, 0x7e, 0x00, 0xb5, 0x68, 0x00, 0x00, 0x00, 0x00,
        0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
    };

    *pixels = {
        0x6A, 0x4E,
    };

    *pixelFormat = PixelFormat(
        PixelFormat::Color::Grayscale,
        PixelFormat::Order::Forward,
        PixelFormat::Alpha::Last);
}

/* Color */
static void
Color(std::vector<uint8_t> *png, std::vector<uint8_t> *pixels, PixelFormat *pixelFormat)
{
    *png = {
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
        0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
        0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00,
        0x0c, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xf8, 0xff, 0x9f, 0x01,
        0x00, 0x04, 0xff, 0x01, 0xff, 0x5d, 0x7d, 0x02, 0x87, 0x00, 0x00, 0x00,
        0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
    };

    *pixels = {
        0xFF, 0xFF, 0x00,
    };

    *pixelFormat = PixelFormat(
        PixelFormat::Color::RGB,
        PixelFormat::Order::Forward,
        PixelFormat::Alpha::None);
}

/* Color Alpha */
static void
ColorAlpha(std::vector<uint8_t> *png, std::vector<uint8_t> *pixels, PixelFormat *pixelFormat)
{
    *png = {
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
        0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
        0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00,
        0x0d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xf8, 0xff, 0x9f, 0x21,
        0x0b, 0x00, 0x07, 0x68, 0x02, 0x69, 0x60, 0xd1, 0x63, 0x78, 0x00, 0x00,
        0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
    };

    *pixels = {
        0xFF, 0xFF, 0x00, 0x6A,
    };

    *pixelFormat = PixelFormat(
        PixelFormat::Color::RGB,
        PixelFormat::Order::Forward,
        PixelFormat::Alpha::Last);
}

/* All Tests */
static void (*PNGTests[])(std::vector<uint8_t> *, std::vector<uint8_t> *, PixelFormat *) = {
    Gray,
    GrayAlpha,
    Color,
    ColorAlpha,
};

TEST(PNG, Read)
{
    for (size_t i = 0; i < sizeof(PNGTests) / sizeof(*PNGTests); i++) {
        /* Load test data. */
        auto const &test = PNGTests[i];
        std::vector<uint8_t> png;
        std::vector<uint8_t> pixels;
        PixelFormat format = PixelFormat(PixelFormat::Color::Grayscale, PixelFormat::Order::Forward, PixelFormat::Alpha::None);
        test(&png, &pixels, &format);

        /* Should be able to read PNG. */
        auto result = PNG::Read(png);
        ASSERT_NE(result.first, ext::nullopt);
        Image const &image = *result.first;

        /* Should have expected pixels in specified format. */
        std::vector<uint8_t> converted = PixelFormat::Convert(image.data(), image.format(), format);
        EXPECT_EQ(converted, pixels);
    }
}

TEST(PNG, Write)
{
    for (size_t i = 0; i < sizeof(PNGTests) / sizeof(*PNGTests); i++) {
        /* Load test data. */
        auto const &test = PNGTests[i];
        std::vector<uint8_t> png;
        std::vector<uint8_t> pixels;
        PixelFormat format = PixelFormat(PixelFormat::Color::Grayscale, PixelFormat::Order::Forward, PixelFormat::Alpha::None);
        test(&png, &pixels, &format);

        /* Should be able to write PNG. */
        auto image = Image(1, 1, format, pixels);
        auto result = PNG::Write(image);
        ASSERT_NE(result.first, ext::nullopt);

        /* Should have expected PNG data. */
        EXPECT_EQ(*result.first, png);
    }
}
